#include "tileMap.h"

#include <iostream>
#include <string>
#include <stdlib.h>

const char* TILES_FILE = "tiles2.txt";

TileMap* TileMap::instance = nullptr;

TileMap::TileMap (): tileMap ()
{
}

TileMap* TileMap::getInstance ()
{
	static bool isInitialized = false;
	if (isInitialized == false) {
		instance = new TileMap ();
		//TODO
		std::ifstream file (TILES_FILE);
		instance->read (file);
		isInitialized = true;
	}

	return instance;
}

Coord TileMap::getSizeInCoord ()
{
	return Coord (tileMap.size (), tileMap[0].size ());
}

int TileMap::countNum (const int num) const
{
    int value = 0;
    for (auto& line : tileMap) {
        for (auto& tile : line) {
            if (tile == num) {
                ++value;
            }

        }
    }
    return value;
}

// Output: 0 for movable tiles, 1 for currently used tiles, 5 for unmovable, previously used tiles.
PrimTileMap TileMap::filter (const int filterableNum) const
{
	PrimTileMap filteredMap = tileMap;
	for (auto& line : filteredMap) {
		for (auto& tile : line) {
            if (tile == filterableNum) {
				tile = 1;
            }
			else {
				tile = 0;
            }
		}
		for (const auto& coord : unMovablePoints) {
            filteredMap[coord.x][coord.y] = 5;
		}
	}
	return filteredMap;
}

// Output: 1 for non-matching tiles, 0 for matching tiles.
PrimTileMap TileMap::inverseFilter (const int filterableNum) const
{
	PrimTileMap filteredMap = tileMap;
	for (auto& line : filteredMap) {
		for (auto& tile : line) {
            if (tile == filterableNum) {
				tile = 0;
            }
			else {
				tile = 1;
            }
		}
	}
	return filteredMap;
}

void TileMap::insertBlockToFill(const PrimTileMap& block)
{
    for (size_t i = 0; i < block.size (); ++i) {
		for (size_t j = 0; j < block[i].size (); ++j) {
            if (block[i][j] == 1) {
                toFill.emplace (Coord(i,j));
            }
		}
	}

}

void TileMap::insertBlockFromFill(const PrimTileMap& block)
{
    for (size_t i = 0; i < block.size (); ++i) {
		for (size_t j = 0; j < block[i].size (); ++j) {
            if (block[i][j] == 1) {
                fromFill.emplace (Coord(i,j));
            }
		}
	}
}

void TileMap::insertCoordToFill (const Coord& coord)
{
    toFill.emplace (coord);
}

void TileMap::insertCoordFromFill (const Coord& coord)
{
    fromFill.emplace (coord);
}

int TileMap::countSteps ()
{
	return stepsDone.size();
}

void TileMap::doStep ()
{
    Coord toCoord = toFill.front();
    Coord fromCoord = fromFill.front();
    toFill.pop();
    fromFill.pop();
    StepType step(toCoord, fromCoord);
    std::swap(tileMap[toCoord.x][toCoord.y], tileMap[fromCoord.x][fromCoord.y]);
    stepsDone.push_back(step);

}

void TileMap::writeSteps (std::ostream& os) const
{
    os << stepsDone.size () << std::endl;
    for (auto& step : stepsDone) {
        os << step.first.x << " " << step.first.y << " " ;
        os << step.second.x << " " << step.second.y << std::endl;
    }
}

void TileMap::writeTileMap (std::ostream& os) const
{
    for (auto& line : tileMap) {
		for (auto& tile : line) {
            os << tile << " ";
		}
		os << std::endl;
	}
}


void TileMap::read (std::ifstream& iStream)
{
	std::string line;
	if (iStream.is_open ()) {
        // TODO: resize with coord.
		std::getline (iStream, line); //skip first line, no one care
		while (std::getline (iStream, line)) {
			tileMap.push_back (std::vector<int> ());

			//TODO: This solution is limited to 1 digit numbers
			for (std::size_t i = 0; i < line.size (); i += 2) {
				tileMap[tileMap.size () - 1].push_back (atoi (&line[i]));
			}
		}
		iStream.close ();
	}
}
